package cn.rengy.tpa.wxmp.util;


import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.fluent.Request;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;

import com.alibaba.fastjson.JSONObject;

import cn.hutool.core.util.ReflectUtil;
import cn.rengy.lang.OriginalValuePropety;
import cn.rengy.tool.http.HttpUtils;
import cn.rengy.tpa.wxmp.exception.WeChatException;
import cn.rengy.tpa.wxmp.pay.exception.SignatureException;
import cn.rengy.tpa.wxmp.token.AccessToken;
import cn.rengy.tpa.wxmp.util.WXPayConstants.SignType;
/**
 * 工具类
 */
public class WeChatUtil {
	private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);
	private static final int tokenInvalidRetry=2;//token错误尝试次数
	
	public static void validResponseSign(SignType signType,String mchKey,Map<String, Object> resultMap)  {
    	String orignalSign = (String)resultMap.get("sign");
        if (!orignalSign.equals(signatureMap(signType,mchKey,resultMap))) {
            throw new SignatureException();
        }
    }
	
	public static String signatureMap(SignType signType,String mchKey,Map<String, Object> data) {
        ArrayList<String> list = new ArrayList<String>();
        for (Map.Entry<String, Object> entry : data.entrySet()) {
        	String key=entry.getKey();
        	String value=entry.getValue()==null?null:entry.getValue().toString();
        	if ("sign".equals(key)||StringUtils.isBlank(value)) {
                continue;
            }
            list.add( key+ "=" + value + "&");
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder signatureTemp = new StringBuilder();
        for (int i = 0; i < size; i++) {
            signatureTemp.append(arrayToSort[i]);
        }
        signatureTemp.append("key=").append(mchKey);
        String signatureSource = signatureTemp.toString();
        //md5Hex
        String signature =null;
        if(signType==SignType.MD5) {
        	signature=DigestUtils.md5Hex(signatureSource).toUpperCase();
        }else if(signType==SignType.HMAC_SHA256){
        	signature=new HmacUtils(HmacAlgorithms.HMAC_SHA_256, mchKey).hmacHex(signatureSource).toUpperCase();
        }
        return signature;
    }
	public static String signatureBean(SignType signType,String mchKey,Object object) {
        Field[] fields=ReflectUtil.getFields(object.getClass());
        // 字典序
        Arrays.sort(fields, new Comparator<Field>() {
            public int compare(Field o1, Field o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        StringBuilder signatureBuilder = new StringBuilder();
        for (Field field : fields) {
        	String fieldName =field.getName();
            // sign不参与签名
            if ("sign".equals(fieldName)) {
                continue;
            }
            OriginalValuePropety originalValuePropety=field.getAnnotation(OriginalValuePropety.class);
            if(originalValuePropety!=null) {
            	fieldName=originalValuePropety.value();
            }
            Object fieldValue = ReflectUtil.getFieldValue(object, field);
            String value=fieldValue==null?null:fieldValue.toString();
            if(StringUtils.isNotBlank(value)) {
            	signatureBuilder.append(fieldName).append("=").append(value).append("&");
            }
        }
        signatureBuilder.append("key=").append(mchKey);
        String signatureSource = signatureBuilder.toString();
        String signature =null;
        if(signType==SignType.MD5) {
        	signature=DigestUtils.md5Hex(signatureSource).toUpperCase();
        }else if(signType==SignType.HMAC_SHA256){
        	signature=new HmacUtils(HmacAlgorithms.HMAC_SHA_256, mchKey).hmacHex(signatureSource).toUpperCase();
        }
        return signature;
    }
	/**
	 * 判断是否请求成功
	 * @param resultStr
	 * @throws WeChatException
	 */
	public static void isSuccess(String resultStr) throws WeChatException{		
		JSONObject jsonObject = JSONObject.parseObject(resultStr);
		Integer errCode =jsonObject.getIntValue("errcode");
		if (errCode!=null && errCode!=0) {
			String errMsg = WeChatReturnCode.getMsg(errCode,jsonObject.getString("errmsg"));
			throw new WeChatException(errCode,errMsg);
		}
	}
	
	public static int getErrCode(String resultStr){		
		JSONObject jsonObject = JSONObject.parseObject(resultStr);
		Integer errCode =jsonObject.getIntValue("errcode");
		return errCode;
	}
	
	
	public static JSONObject postSuccess(String url,String data,AccessToken accessToken){
		return requestSuccess(true,url,data,accessToken);
	}
	
	public static JSONObject getSuccess(String url,AccessToken accessToken){
		return requestSuccess(false,url,null,accessToken);
	}
	
	/**
	 * 发送请求并成功响应
	 * @param url
	 * @param data
	 * @param accessToken
	 * @return
	 */
	private static JSONObject requestSuccess(boolean post,String url,String data,AccessToken accessToken){
		for(int i=0;i<tokenInvalidRetry;i++){
			String resultStr=null;
			if(post) {
				resultStr = HttpUtils.post(url+accessToken.token(), data);
			}else {
				resultStr = HttpUtils.get(url+accessToken.token());
			}
			
			JSONObject resultObject=JSONObject.parseObject(resultStr);
			int errCode =resultObject.getIntValue("errcode");
			if(errCode==0){
				return resultObject;
			}else if(isAccessTokenInvalid(errCode)){
				accessToken.refresh();
			}else{
				String errMsg = resultObject.getString("errmsg");
				throw new WeChatException(errCode,errMsg);
			}
		}
		return null;
	}
	
	/**
	 * https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234
	 * 判断 API 请求结果失败是否由于 access_token 无效引起的
	 * 无效可能的情况 error_code 值：
	 * 40001 = 获取access_token时AppSecret错误，或者access_token无效(刷新后也可以引起老access_token失效)
	 * 42001 = access_token超时
	 * 42002 = refresh_token超时
	 * 40014 = 不合法的access_token
	 */
	public static boolean isAccessTokenInvalid(int errCode) {
		return (errCode == 40001 || errCode == 42001 || errCode == 42002 || errCode == 40014);
	}
	
	public static void getJson(String url,AccessToken accessToken,String data) throws ClientProtocolException, IOException {
		Request request = Request.Get(url+accessToken.token());
		HttpResponse httpResponse = request.execute().returnResponse();
		HttpEntity httpEntity=httpResponse.getEntity();
		String contentType=httpEntity.getContentType().getValue();
		MediaType mediaType=MediaType.valueOf(contentType);
		if(MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)||MediaType.TEXT_PLAIN.isCompatibleWith(mediaType)) {
			String jsonString=EntityUtils.toString(httpEntity,Consts.UTF_8);
			JSONObject resultObject=JSONObject.parseObject(jsonString);
			
		}else {
			throw new WeChatException("返回类型是："+contentType); 
		}
		
	}
}
